#include "item.h"
#include "creature.h"
#include "map.h"
#include "game.h"
#include "random.h"
#include "buffs.h"

Item::ItemActor::ItemActor( Item* item )
: _item( item )
{
}

void Item::ItemActor::SetItem( Item* item )
{
    _item = item;
}

Item* Item::ItemActor::GetItem() const
{
    return _item;
}

void Item::ItemActor::Destroy()
{
    if ( _item )
        _item->DestroyPrivate();
    Actor::Destroy();
}

void Item::ItemActor::DestroyPrivate()
{
    Actor::Destroy();
}

bool Item::ItemActor::HasBuff( const Buff* buff ) const
{
    if ( _item )
        return _item->HasBuff( buff );
    return false;
}

void Item::ItemActor::GetAllBuffs( BuffList& outputList )
{
    if ( _item )
        _item->GetAllBuffs( outputList );
}

void Item::ItemActor::GetAllBuffs( ConstBuffList& outputList ) const
{
    if ( _item )
        _item->GetAllBuffs( outputList );
}

void Item::ItemActor::Add( Buff* buff )
{
    if ( _item )
        _item->Add( buff );
    else
        Actor::Add( buff );
}

void Item::ItemActor::Remove( Buff* buff )
{
    if ( _item )
        _item->Remove( buff );
    else
        Actor::Remove( buff );
}

const Behaved* Item::ItemActor::GetBase() const
{
    if ( _item )
        return _item;
    return this;
}

Behaved* Item::ItemActor::GetBase()
{
    if ( _item )
        return _item;
    return this;
}

Item::Item()
: _actor( 0 )
, _itemActor( 0 )
, _holdBuff( 0 )
, _wearBuff( 0 )
, _wieldBuff( 0 )
, _destroy( false )
{
}

Item::~Item()
{
}

void Item::SetMap( Map* map, uint x, uint y )
{
    if ( _actor ) {
        if ( _holdBuff ) {
            _holdBuff->SetSourceItem( 0 );
            _holdBuff->Destroy();
            _holdBuff = 0;
        }
        Wear( false );
        Wield( false );
        _actor->RemoveItem( this );
        _actor = 0;
    }
    if ( !_itemActor )
        _itemActor = new ItemActor( this );
    _itemActor->SetMap( map, x, y );
}

const Map* Item::GetMap() const
{
    if ( _actor )
        return _actor->GetMap();
    if ( _itemActor )
        return _itemActor->GetMap();
    throw std::runtime_error( "Getting map of an unattached item" );
}

Map* Item::GetMap()
{
    if ( _actor )
        return _actor->GetMap();
    if ( _itemActor )
        return _itemActor->GetMap();
    throw std::runtime_error( "Getting map of an unattached item" );
}

void Item::SetActor( Creature* c )
{
    if ( _actor ) {
        if ( _holdBuff ) {
            _holdBuff->SetSourceItem( 0 );
            _holdBuff->Destroy();
            _holdBuff = 0;
        }
        Wear( false );
        Wield( false );
        _actor->RemoveItem( this );
        _actor = 0;
    }
    if ( _itemActor ) {
        _itemActor->SetItem( 0 );
        _itemActor->DestroyPrivate();
        _itemActor = 0;
    }
    if ( !c->GetMap() )
        throw std::runtime_error( "Attaching item to unattached actor" );
    _actor = c;
    _actor->AddItem( this );
    CompoundBuff* b = 0;
    Call< BICreateHoldBuff >( &b );
    _holdBuff = b;
    _holdBuff->SetSourceItem( this );
    _holdBuff->SetTarget( _actor );
}

Creature* Item::GetActor() const
{
    return _actor;
}

void Item::Wear( bool wear )
{
    uint type;
    Call< BIItemType >( &type );
    if ( wear && !IsWorn() && !IsWielded() && type & BIItemType::WEARABLE ) {
        CompoundBuff* b = 0;
        Call< BICreateWearBuff >( &b );
        _wearBuff = b;
        _wearBuff->SetSourceItem( this );
        _wearBuff->SetTarget( _actor );
        Call< BIOnEquip >( false );
    }
    if ( !wear && IsWorn() ) {
        _wearBuff->SetSourceItem( 0 );
        _wearBuff->Destroy();
        _wearBuff = 0;
    }
}

bool Item::IsWorn() const
{
    return _wearBuff != 0;
}

void Item::Wield( bool wield )
{
    uint type;
    Call< BIItemType >( &type );
    if ( wield && !IsWielded() && !IsWorn() && type & BIItemType::WIELDABLE ) {
        CompoundBuff* b = 0;
        Call< BICreateWieldBuff >( &b );
        _wieldBuff = b;
        _wieldBuff->SetSourceItem( this );
        _wieldBuff->SetTarget( _actor );
        Call< BIOnEquip >( true );
    }
    if ( !wield && IsWielded() ) {
        _wieldBuff->SetSourceItem( 0 );
        _wieldBuff->Destroy();
        _wieldBuff = 0;
    }
}

bool Item::IsWielded() const
{
    return _wieldBuff != 0;
}

void Item::Destroy()
{
    DestroyPrivate();
    if ( _itemActor )
        _itemActor->DestroyPrivate();
}

void Item::DestroyPrivate()
{
    if ( IsDestroyed() )
        throw std::runtime_error( "Destroying a destroyed item" );
    BuffList t;
    GetAllBuffs( t );
    foreach ( Buff* b, t )
        b->Destroy();
    _destroy = true;

    if ( _actor ) {
        if ( _holdBuff ) {
            _holdBuff->SetSourceItem( 0 );
            _holdBuff->Destroy();
            _holdBuff = 0;
        }
        Wear( false );
        Wield( false );
        _actor->GetMap()->GetGame().DestroyItem( this );
        _actor->RemoveItem( this );
        _actor = 0;
    }
    else if ( _itemActor ) {
        _itemActor->GetMap()->GetGame().DestroyItem( this );
    }
    else
        throw std::runtime_error( "Destroying unattached item" );

}

bool Item::IsDestroyed() const
{
    return _destroy;
}

bool Item::PrioritySort( const Item* less, const Item* greater )
{
    int lp, gp;
    less->Call< BIPriority >( &lp );
    greater->Call< BIPriority >( &gp );
    if ( lp == gp )
        return less < greater;
    return lp > gp;
}

bool Item::CategorySort( const Item* less, const Item* greater )
{
    uint lc, gc;
    less->Call< BIItemCategory >( &lc );
    greater->Call< BIItemCategory >( &gc );
    if ( lc == gc ) {
        int lp, gp;
        less->Call< BIPriority >( &lp );
        greater->Call< BIPriority >( &gp );
        if ( lp == gp )
            return less < greater;
        return lp > gp;
    }
    return lc < gc;
}

std::string Item::CategoryToString( uint category )
{
    if ( category == BIItemCategory::WEAPON )
        return "Weapons";
    if ( category == BIItemCategory::ARMOUR )
        return "Armour";
    if ( category == BIItemCategory::ACCESSORY )
        return "Accessories";
    if ( category == BIItemCategory::CONSUMABLE )
        return "Consumables";
    if ( category == BIItemCategory::RUNE )
        return "Runes";
    if ( category == BIItemCategory::OTHER )
        return "Other";
    return "";
}

bool Item::IsItem( Item** item ) const
{
    *item = const_cast< Item* >( this );
    return true;
}

bool Item::IsActor( Actor** actor ) const
{
    *actor = _itemActor;
    return true;
}

bool Item::ItemCarrier( Creature** carrier, bool* worn, bool* wielded ) const
{
    *carrier = _actor;
    *worn    = IsWorn();
    *wielded = IsWielded();
    return true;
}

bool Item::Symbol( IO::SymList* list ) const
{
    IO::Sym symbol;
    Call< BIItemSymbol >( &symbol );
    list->push_back( symbol );
    return true;
}

bool Item::Name( std::string* name, bool* plural, uint article ) const
{
    if ( name->empty() && article == ARTICLE_INDEFINITE )
        *name += "an ";
    if ( article == ARTICLE_PLURAL )
        *name += "unknown items";
    else
        *name += "unknown item";
    return true;
}

bool Item::OnSetFire()
{
    uint flammable;
    Call< BIFlammable >( &flammable );

    if ( flammable & BIFlammable::FLAMMABLE_BURN_INDEFINITE ) {
        ( new OnFireBuff() )->SetTarget( this );
        return true;
    }
    if ( flammable & BIFlammable::FLAMMABLE_BURN_EXTRA_LONG ) {
        Buff* b = new OnFireBuff();
        b->StartTimer( Random::Int( EXTRA_LONG_DICE ) );
        b->SetTarget( this );
        return true;
    }
    if ( flammable & BIFlammable::FLAMMABLE_BURN_LONG ) {
        Buff* b = new OnFireBuff();
        b->StartTimer( Random::Int( LONG_DICE ) );
        b->SetTarget( this );
        return true;
    }
    if ( flammable & BIFlammable::FLAMMABLE_BURN_SHORT ) {
        Buff* b = new OnFireBuff();
        b->StartTimer( Random::Int( SHORT_DICE ) );
        b->SetTarget( this );
        return true;
    }
    if ( flammable & BIFlammable::FLAMMABLE_BURN_EXTRA_SHORT ) {
        Buff* b = new OnFireBuff();
        b->StartTimer( Random::Int( EXTRA_SHORT_DICE ) );
        b->SetTarget( this );
        return true;
    }

    if ( flammable & BIFlammable::FLAMMABLE_CHAR )
        ( new CharredBuff() )->SetTarget( this );

    if ( flammable & BIFlammable::FLAMMABLE_SMOULDER_LONG ) {
        Buff* b = new SmoulderingBuff();
        b->StartTimer( Random::Int( LONG_DICE ) );
        b->SetTarget( this );
        return true;
    }
    if ( flammable & BIFlammable::FLAMMABLE_SMOULDER_SHORT ) {
        Buff* b = new SmoulderingBuff();
        b->StartTimer( Random::Int( SHORT_DICE ) );
        b->SetTarget( this );
        return true;
    }

    if ( flammable & BIFlammable::FLAMMABLE_DESTROY ) {
        if ( !IsDestroyed() ) {
            Bank().Unary( UNARY_FIRE_DESTROY_INSTANT, this );
            Destroy();
        }
    }
    return true;
}

bool Item::OnUpdateBuffs()
{
    if ( !_actor )
        return true;
    if ( _holdBuff ) {
        _holdBuff->SetSourceItem( 0 );
        _holdBuff->Destroy();
        CompoundBuff* b = 0;
        Call< BICreateHoldBuff >( &b );
        _holdBuff = b;
        _holdBuff->SetSourceItem( this );
        _holdBuff->SetTarget( _actor );
    }
    if ( _wieldBuff ) {
        _wieldBuff->SetSourceItem( 0 );
        _wieldBuff->Destroy();
        CompoundBuff* b = 0;
        Call< BICreateWieldBuff >( &b );
        _wieldBuff = b;
        _wieldBuff->SetSourceItem( this );
        _wieldBuff->SetTarget( _actor );
    }
    if ( _wearBuff ) {
        _wearBuff->SetSourceItem( 0 );
        _wearBuff->Destroy();
        CompoundBuff* b = 0;
        Call< BICreateWearBuff >( &b );
        _wearBuff = b;
        _wearBuff->SetSourceItem( this );
        _wearBuff->SetTarget( _actor );
    }
    return true;
}

bool Item::MemorySymbol( bool* memory, bool* tileMemory ) const
{
    *memory = true;
    return true;
}